(function() {
    
    function insert(type, attributes) {
        var e = document.createElement(type), a;
        for(a in attributes) e[a] = attributes[a];
        return e;
    }

    var head = document.getElementsByTagName("head")[0];
    head.appendChild(insert("script", {
        type: 'text/javascript',
           // http://maps.google.com/maps/api/geocode/xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true_or_false
        src: 'http://maps.google.com/maps/api/js?sensor=true&callback=tut.maps.ready',
        async: 'true'
    }));
    head.appendChild(insert("link", {
        type: 'text/css',
        href: 'tut-style.css',
        rel: 'stylesheet'
    }));
})();

window.log = function(message) {
    console.log?console.log(message):alert(message);
};

window.tut = {
		
    ds: [47.662098,9.49896],
    station: {
        konstanz: [47.665344,9.217937],
        boehringen: [47.659852,9.499054]
    },
    maps: {
        _isReady: false,
        _wasInvoked: false,
        _canvasElement: undefined,
        canvas: function(elem) {
            if(!elem) return this.canvasElement;
            else {
                this.canvasElement = elem;
                window.addEventListener("offline", function() {
                    elem.innerHTML = "<div class='note'>connection went down</div>";
                }, false);
                if(this.isReady && !this.wasInvoked) return this.drawMap();
                else if(this.isReady && this.wasInvoked) return this.drawRoute();
            }
        },
        _origin: undefined,
        _dirDisplay: undefined,
        _dirService: undefined,
        _map: 0,
        originalStation: 'boehringen',
        ready: function() {
            this.isReady = true;

            var c = tut.station[this.originalStation]||tut.station.boehringen;
            this.origin = new google.maps.LatLng(c[0], c[1]);

            if(!this.wasInvoked && this.canvas()) return this.drawMap();
            else if(this.wasInvoked && this.canvas()) return this.drawRoute();
        },
        getClosestStation: function(lat, lng) {
            var minDiffLat = 91, minDiffLng = 181, city, station, closestCity;
            for(city in tut.station) {
                station = tut.station[city];
                var diffLat = Math.abs(lat-station[0]),
                diffLng = Math.abs(lng-station[1]);
                if(diffLat < minDiffLat && diffLng < minDiffLng) {
                    minDiffLat = diffLat;
                    minDiffLng = diffLng;
                    closestCity = city;
                }
            }
            log("closest station is: " + closestCity);
            station = tut.station[closestCity];
            this.origin = new google.maps.LatLng(station[0], station[1]);
        },
        route: function() {
            var l = localStorage;
            if(!l.lat || !l.lng) return "Missing position data";
            else if(!this.canvas()) return "Missing canvas object";
            this.wasInvoked = true;

            if(!this.isReady && this.canvas()) return this.canvas().innerHTML = "<div class='note'>The Map is still loading or a problem occured.</div>";
            else if(this.isReady && this.canvas()) return this.drawRoute();
        },
        drawMap: function() {
            // The final functions, called from within this object
            // If drawMap is called, canvas and Maps is ready, but not the route
            this.map = new google.maps.Map(this.canvas(), {
                zoom: 12,
                center: this.origin,
                mapTypeId: google.maps.MapTypeId.ROADMAP,
                navigationControl: false,
                mapTypeControl: false,
                scaleControl: false
            });
            return "Map drawn";
        },
        drawRoute: function() {
            // The final functions, called from within this object
            // if drawRoute is called, canvas, Maps and Position-data is available
            this.getClosestStation(localStorage.lat, localStorage.lng);
            this.drawMap();
            var dirDisplay = new google.maps.DirectionsRenderer(),
            dirService = new google.maps.DirectionsService(),
            destination = new google.maps.LatLng(localStorage.lat, localStorage.lng);
            dirDisplay.setMap(this.map);
            var request = {
                origin: this.origin,
                destination: destination,
                travelMode: google.maps.DirectionsTravelMode.DRIVING
            };
            dirService.route(request, function(result, status) {
                switch(status) {
                    case google.maps.DirectionsStatus.OK:
                        dirDisplay.setDirections(result);
                        break;
                    case google.maps.DirectionsStatus.NOT_FOUND:
                        log("Your place was not found");
                        break;
                    case google.maps.DirectionsStatus.ZERO_RESULTS:
                        log("Zero results")
                        break;
                    default:
                        log("An unknown error occurred.")
                        break;
                }
            });
            return "Route drawn";
        }
    },
    canvas: {
        fillColor: undefined,
        strokeColor: undefined,
        gradient: undefined,
        ctx: undefined,
        canvasElement: undefined,
        getContext: function(elem) {
            if(!elem || !elem.getContext) {
                log("canvas.getContext: No element given or no canvas-support");
                return;
            }
            this.canvasElement = elem;
            return this.ctx = elem.getContext("2d");
        },
        createGradient: function(from, to, colors) {
            if(from.length<2 || to.length<2 || colors.length<2 || !this.ctx) {
                log("canvas.createGradient: No context available or wrong parameters");
                return this;
            }
            var grd = this.ctx.createLinearGradient(from[0], from[1], to[0], to[1]);
            for(var i=0; i<colors.length; i++) grd.addColorStop(i/(colors.length-1), colors[i]);
            this.gradient = grd;
            return this;
        },
        triangle: function(canvas) {
            if(canvas.nodeType != 1 || !canvas.getContext) {
                log("canvas.triangle: Wrong node Type or no canvas-support");
                return this;
            }
            var width = canvas.width, height = canvas.height, lw,
            ctx = this.getContext(canvas);
            lw = ctx.lineWidth;

            // draw
            this.polygon([
                width*0.5, lw,
                width, height-lw,
                0, height
                ]);
            this.fillStroke(true);
            return this;
        },
        clearRect: function(x,y,w,h) {
            if(!this.ctx || this.clearRect.arguments.length < 4) {
                log("canvas.clearRect: No context available or not enough parameters");
                return this;
            }
            if(!(/MSIE/.test(navigator.userAgent) && !window.opera))
                this.ctx.clearRext(x,y,w,h);
            return this;
        },
        rect: function(a, b) {
            if(a.length < 2 || b.length < 2) {
                log("canvas.rect: Not enough parameters");
                return this;
            }
            return this.polygon([
                a[0], a[1],
                b[0], a[1],
                b[0], b[1],
                a[0], b[1]
                ]);
        },
        fillStroke: function(stroke) {
            if(!this.ctx) {
                log("canvas.fillStroke: No context available");
                return this;
            }
            var ctx = this.ctx;
            if(this.gradient) {
                ctx.fillStyle = this.gradient;
                ctx.fill();
            } else if(this.fillColor) {
                ctx.fillStyle = this.fillColor;
                ctx.fill();
            }
            if(this.strokeColor && stroke) {
                ctx.strokeStyle = this.strokeColor;
                ctx.stroke();
            }
            return this;
        },
        house: function(width, height) {
            height = height/2;
            var container = document.createElement("div"), halfWidth = width/2;
            var roofDeg = Math.asin(halfWidth/height) * 180/Math.PI, translate;
            container.style.width = width + "px";
            for(var i=0; i<4; i++) {
                var roof = document.createElement("canvas");
                roof.className = "tut-house-roof";
                roof.width = width;
                roof.height = height;
                this.strokeColor = "hsl(199, 80%, 55%)";
                this.getContext(roof);
                this.createGradient([0,0], [width, height/2], ['hsl(193, 90%, 60%)','hsl(205, 70%, 53%)']).triangle(roof);
                if(i%2 == 0) translate = "translateZ(";
                else translate = "translateX(";
                if(i>1) translate += "-";
                roof.style.webkitTransform = translate + halfWidth + "px) rotateY("+(i*90)+"deg) rotateX("+roofDeg+"deg)";
                container.appendChild(roof);

                var front = document.createElement("div");
                front.className = "tut-house-front";
                front.style.width = (width*0.8) + "px";
                front.style.height = height + "px";
                var t = "translate3d";
                switch(i) {
                    case 0:
                        t+="("  + (width*0.1)     + "px," + (height*0.9) + "px, "+(halfWidth*0.8)+"px) ";
                        break;
                    case 1:
                        t+="("  + halfWidth       + "px," + (height*0.9) + "px, 0px) ";
                        break;
                    case 2:
                        t+="("  + (width*0.1)     + "px," + (height*0.9) + "px, -"+(halfWidth*0.8)+"px) ";
                        break;
                    case 3:
                        t+="(-" + (halfWidth*0.6) + "px," + (height*0.9) + "px, 0px) ";
                        break;
                }
                front.style.webkitTransform = t + "rotateY("+(i*90)+"deg)";
                container.appendChild(front);
            }
            return container;
        },
        polygon: function(points) {
            if(!this.ctx || points.length < 3) {
                log("canvas.polygon: No context available or not enough parameters");
                return false;
            }
            var ctx = this.ctx;
            ctx.beginPath();
            ctx.moveTo(points[0], points[1]);
            for(var i = 2; i < points.length; i++) {
                ctx.lineTo(points[i], points[++i]);
            }
            ctx.closePath();
            return this;
        },
        ParticleAnimation: function(canvas, grd) {
            if(!canvas) return;
            grd = grd||this.gradient||"#000";
            
            var particles, NUM_PARTICLES, size, i, changeYDir, changeXDir,
            changeYDirDir, changeXDirDir, lastTimes, refreshMilliseconds,
            isBusy, isOver,
            ctx = canvas.getContext('2d'), width = canvas.width, height = canvas.height;

            function init() {
                particles = [], NUM_PARTICLES = 8000, size = 2, i,
                changeYDir = 0.1, changeXDir = -0.01,
                changeYDirDir = 0.003, changeXDirDir = Math.random() * 0.01 - 0.005,
                // Wenn alle particles verschwunden sind,
                // soll noch 25 mal der Farbverlauf aktualisiert
                // werden, um ein Fadeout zu erzeugen
                lastTimes = 25,
                // Nach wie vielen Sekunden soll der nächste Frame
                // gezeichnet werden?
                refreshMilliseconds = 25,
                isBusy = false, isOver = false;
                for(i = 0; i < NUM_PARTICLES; i++) particles[i] = new Particle();
            }

            init();
            function Particle() {
                this.x = Math.random() * (width - 2*size);
                this.y = Math.random() * (height - 2*size);

                this.xDir = Math.random() * 2 - 1;
                this.yDir = Math.random() * 2 - 1;

                this.update = function() {
                    this.x += this.xDir;
                    this.y += this.yDir;

                    this.yDir += changeYDir;
                    //this.xDir += changeXDir;

                    if(this.x > width - size || this.x < 0) {
                        this.xDir = -this.xDir;
                    }
                    if(this.y > height - size || this.y < 0) {
                        this.yDir = -this.yDir;
                    }

                };
            }

            function loop() {
                ctx.fillStyle = grd;
                ctx.fillRect(0,0,width, height);

                if(NUM_PARTICLES == 0) {
                    if(lastTimes > 0) {
                        lastTimes--;
                        setTimeout(loop, refreshMilliseconds);
                    } else {
                        isBusy = false;
                        isOver = true;
                    }
                    return;
                }

                ctx.fillStyle = "#fff";

                for(i = 0; i < NUM_PARTICLES; i++) {
                    particles[i].update();
                    if(particles[i].y + size < 0 || particles[i].y > height ||
                        particles[i].x + size < 0 || particles[i].x > width) {
                        particles.splice(i, 1);
                        NUM_PARTICLES--;
                    } else {
                        ctx.fillRect(particles[i].x, particles[i].y, size, size);
                    }
                }

                changeYDir += changeYDirDir;
                changeXDir += changeXDirDir;

                if(changeYDir > 0.3 || changeYDir < -0.4) changeYDirDir = -changeYDirDir;
                if(changeXDir > 0.08 || changeXDir < -0.1) changeXDirDir = -changeXDirDir;

                if(isBusy) {
                    setTimeout(loop, refreshMilliseconds);
                }
            }

            this.togglePlay = function() {
                if(isBusy) {
                    isBusy = false;
                } else if(!isOver) {
                    isBusy = true;
                    loop();
                } else {
                    init();
                    isBusy = true;
                    loop();
                }
            };
        }
    }
};